package com.naiterui.ehp.bp.support.config;

import java.util.List;
import java.util.Properties;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;

import javax.annotation.PostConstruct;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.sleuth.instrument.async.LazyTraceThreadPoolTaskExecutor;
import org.springframework.cloud.sleuth.instrument.async.TraceableScheduledExecutorService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Profile;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;

import com.alibaba.cloud.nacos.NacosConfigProperties;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.naiterui.ehp.bp.support.filter.UserTokenHandler;
import com.naiterui.ehp.bp.support.security.HeaderConstant;

import feign.Request;
import feign.RequestInterceptor;
import feign.Retryer;
import lombok.SneakyThrows;

/**
 * 程序基础配置
 *
 * @author 王泽浩
 * @date 2018/5/22 下午5:51
 * @since 1.0.0
 */
public class AppConfiguration {
    private static final Logger LOGGER = LoggerFactory.getLogger(AppConfiguration.class);

    @Autowired
    private RequestMappingHandlerAdapter handlerAdapter;

    @PostConstruct
    public void init() {
        ConfigurableWebBindingInitializer initializer =
            (ConfigurableWebBindingInitializer)handlerAdapter.getWebBindingInitializer();
        assert initializer != null;
        if (initializer.getConversionService() != null) {
            GenericConversionService genericConversionService =
                (GenericConversionService)initializer.getConversionService();

            genericConversionService.addConverter(new DateConverterConfig());
        }
    }

    /**
     * 自定义异步线程池
     *
     * @return 异步线程池
     */
    @Bean
    public TaskExecutor taskExecutor(BeanFactory beanFactory) {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setThreadNamePrefix("async-pool-");
        // 核心线程数
        executor.setCorePoolSize(10);
        // 最大线程数
        executor.setMaxPoolSize(100);
        // 队列最大长度
        executor.setQueueCapacity(200);
        // 线程池维护线程所允许的空闲时间
        executor.setKeepAliveSeconds(300);
        // 线程池对拒绝任务(无线程可用)的处理策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        LOGGER.info("初始化线程池 {}", executor);
        return new LazyTraceThreadPoolTaskExecutor(beanFactory, executor);
    }

    @SneakyThrows
    @Bean
    public ConfigService configService(NacosConfigProperties nacosProperties) {
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, nacosProperties.getServerAddr());
        properties.put(PropertyKeyConst.USERNAME, nacosProperties.getUsername());
        properties.put(PropertyKeyConst.PASSWORD, nacosProperties.getPassword());
        properties.put(PropertyKeyConst.NAMESPACE, nacosProperties.getNamespace());
        return NacosFactory.createConfigService(properties);
    }

    @Bean
    public ScheduledExecutorService scheduledExecutorService(BeanFactory beanFactory) {
        ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("scheduled-pool-%d").build();
        ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(10, threadFactory);
        LOGGER.info("初始化线程池 {}", scheduledExecutorService);
        return new TraceableScheduledExecutorService(beanFactory, scheduledExecutorService);
    }

    @Bean
    @Profile("dev")
    public feign.Logger.Level feignLoggerLevel() {
        return feign.Logger.Level.FULL;
    }

    /**
     * 重试
     */
    @Bean
    public Retryer feignRetry() {
        return new Retryer.Default();
    }

    /**
     * 设置请求的连接和处理的超时时间
     */
    @Bean
    public Request.Options feignRequestOptions() {
        return new Request.Options(5000, 5000);
    }

    /**
     * 由于使用eureka+feign，所以请求的时候使用的是IP:PORT请求，所以需要在header中增加X-inner
     *
     * @return /
     */
    @Bean
    @ConditionalOnMissingBean(RequestInterceptor.class)
    public RequestInterceptor feignInterceptor() {
        return template -> {
            template.header("X-inner", "1");
            template.header(HeaderConstant.DATA_NO_WRAP, "1");
        };
    }

    /**
     * UserToken处理 对应需要校验的服务类注入该bean并实例化具体的 userIdKey、userIdValue、忽略目录、token校验方法等内容。
     * 
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    UserTokenHandler userTokenHandler() {
        String userIdKey = "userId";
        List<String> excludePaths = Lists.newArrayList("/pc/**");
        return new UserTokenHandler(userIdKey, excludePaths) {
            @Override
            public boolean checkToken(String token) {
                return false;
            }
        };
    }

}
